namespace cvm
{
	void VM::Dispatch();
	{
		switch (*ic++)
		{
		case op_noop:
			break;
		case op_int_8:
			PushConstant<int, 1>();
			break;
		case op_int_16:
			PushConstant<int, 2>();
			break;
		case op_int_32:
			PushConstant<int, 4>();
			break;
		case op_int_64:
			break;
		case op_int_128:
			Unimplemented();
			break;
		case op_uint_8:
			PushConstant<unsigned int, 1>();
			break;
		case op_uint_16:
			PushConstant<unsigned int, 2>();
			break;
		case op_uint_32:
			PushConstant<unsigned int, 4>();
			break;
		case op_uint_64:
			Unimplemented();
			break;
		case op_uint_128:
			Unimplemented();
			break;
		case op_flt_32:
			PushConstant<float, 4>();
			break;
		case op_flt_64:
			PushConstant<double, 8>();
			break;
		case op_char:
			Push(*ic++);
			break;
		case op_wchar:
			PushConstant<wchar_t, 2>();
		case op_type_of:
			Unimplemented();
			break;
		case op_prim_type:
			Unimplemented();
			break;
		case op_cast_to:
			Unimplemented();
			break;
		case op_cast_prim:
			Unimplemented();
			break;
		case op_push_string:
			Push(GetString(GetConstant<unsigned int, 2>()));
			break;
		case op_push_wstring:
			Unimplemented();
			break;
		case op_push_sub:
			Push(Function());
			break;
		case op_push_true:
			Push(true);
			break;
		case op_push_false:
			Push(false);
			break;
		case op_pop:
			Pop();
			break;
		case op_popN:
			PopN(*ic++);
			break;
		case op_zapN:	
			DigN(*ic++);
			Pop();
			break;
		case op_dup:
			Dup();
			break;
		case op_dupN:
			DupN();
			break;
		case op_swap:
			Swap();
			break;
		case op_swapN:
			SwapN(*ic++);
			break;
		case op_getN:
			GetN(*ic++);
			break;
		case op_setN:
			SetN(*ic++);
			break;
		case op_digN:
			DigN(*ic++);
			break;
		case op_buryN:
			BuryN(*ic++);
			break;
	    case op_lt:
			F2(IArithmetic, Lt);
			break;
		case op_gt:
			F2(IArithmetic, Gt);
			break;
		case op_lteq:
			F2(IArithmetic, LtEq);
			break;
		case op_gteq:
			F2(IArithmetic, GtEq);
			break;
		case op_eq:
			F2(IArithmetic, Eq);
			break;
		case op_neq:
			F2(IArithmetic, NEq);
			break;
		case op_add:
			F2(IArithmetic, Add);
			break;
		case op_sub:
			F2(IArithmetic, Sub);
			break;
		case op_mul:
			F2(IArithmetic, Mul);
			break;
		case op_div:
			F2(IArithmetic, Div);
			break;
		case op_mod:
			F2(IArithmetic, Mod);
			break;
		case op_inc:
			F1(IOrdinal, Inc);
			break;
		case op_dec:
			F1(IOrdinal, Dec);
			break;
		case op_lt_int:
			F2(Int, Lt);
			break;
		case op_gt_int:
			F2(Int, Gt);
			break;
		case op_lteq_int:
			F2(Int, LtEq);
			break;
		case op_gteq_int:
			F2(Int, GtEq);
			break;
		case op_add_int:
			F2(Int, Add);
			break;
		case op_sub_int:
			F2(Int, Sub);
			break;
		case op_mul_int:
			F2(Int, Mul);
			break;
		case op_div_int:
			F2(Int, Div);
			break;
		case op_mod_int:
			F2(Int, Mod);
			break;
		case op_inc:
			F2(Int, Inc);
			break;
		case op_dec:
			F2(Int, Dec);
			break;
		case op_lt_flt:
			F2(Flt, Lt);
			break;
		case op_gt_flt:
			F2(Flt, Gt);
			break;
		case op_lteq_flt:
			F2(Flt, LtEq);
			break;
		case op_gteq_flt:
			F2(Flt, GtEq);
			break;
		case op_add_flt:
			F2(Flt, Add);
			break;
		case op_sub_flt:
			F2(Flt, Sub);
			break;
		case op_mul_flt:
			F2(Flt, Mul);
			break;
		case op_div_flt:
			F2(Flt, Div);
			break;
		case op_mod_flt:
			F2(Flt, Mod);
			break;
		case op_not:
			F1(IBits, Not);
			break;
		case op_or:
			F2(IBits, Or);
			break;
		case op_nor:
			F2(IBits, NOr);
			break;
		case op_xor:
			F2(IBits, XOr);
			break;
		case op_and:
			F2(IBits, And);
			break;
		case op_nand:
			F2(IBits, NAnd);
			break;
		case op_apply: 
			Apply();
			break;
		case op_dip:
			Dip();
			break;
		case op_sap:
			Swap();
			Dip();
			Apply();
			break;	
		case op_while:
			{
				PULL(IFunction, body);
				PULL(IFunction, cond);
				cond.Eval(*this);
				while (top.As<bool>()) {
					body.Eval(*this);
					cond.Eval(*this);
				}
			}
			break;
		case op_whilene:
			Unimplemented();
			break;
		case op_whilenz:
			Unimplemented();
			break;
		case op_for:
			Unimplemented();
			break;
		case op_foreach:
			Unimplemented();
			break;
		case op_foreach_index:
			Unimplemented();
			break;
		case op_forN:
			Unimplemented();
			break;
		case op_if_else:
			{
				PULL(IFunction, onfalse);
				PULL(IFunction, ontrue);
				PULL(Bool, cond);
				if (cond.As<bool>()) {
					onfalse->DecRef();
					Eval(ontrue);
				}
				else {
					ontrue->DecRef();
					Eval(onfalse);
				}
			}
			break;
		case op_if:
			Unimplemented();
			break;
		case op_repeat:
			Unimplemented();
			break;
		case op_lin_rec:
			Unimplemented();
			break;
		case op_bin_rec:
			Unimplemented();
			break;
		case op_ret:
			Ret();
			return;				
		case op_tail_apply:		
			TailApply();
			break;
		case op_quote:
			Quote();
			break;
		case op_papply:
			PApply();
			break;
		case op_papply2:
			PApply(); PApply();
			break;
		case op_papply3:
			PApply(); PApply(); PApply();
			break;
		case op_compose:
			Compose();
			break;
		case op_get_slot:
		case op_set_slot:
		case op_get_slot_index:
		case op_set_slot_index:
		case op_add_slot:
		case op_num_slots:
		case op_new:
		case op_free:
		case op_null:
		case op_new_array:
		case op_tuple:
		case op_nil:
		case op_count:
		case op_get_at,
		case op_set_at:
		case op_transform_at:
		case op_swap_at:
		case op_first:
		case op_rest:
		case op_last:
		case op_uncons:
		case op_rev:
		case op_fold:
		case op_reduce:
		case op_map:
		case op_filter:
		case op_filter_map:
		case op_range:
		case op_empty:
		case op_nats: 
		case op_prepend:
		case op_append:
		case op_slice:
		case op_gen:
		case op_external:
			Unimplemented();
			break;
		case op_push_neg1:
			Push(-1);
			break;
		case op_push0:
			Push(0);
			break;
		case op_push1:
			Push(1);
			break;
		case op_push2:
			Push(2);
			break;
		case op_dup2:
			Dup();Dup();
			break;
		case op_dup3:
			Dup(); Dup(); Dup();
			break;
		case op_dupd:
			PullTo(tmp); Dup(); PushFrom(tmp);
			break;
		case op_popd:
			Pull(tmp); Dup(); PushFrom(tmp);
			break;
		case op_pop2:
			Pop(); Pop();
			break;
		case op_pop3:
			Pop(); Pop(); Pop();
			break;
		case op_pop4:
			Pop(); Pop(); Pop(); Pop();
			break;
		case op_swap2:
			SwapN(2);
			break;
		case op_swap3:
			SwapN(3);
			break;
		case op_swap4:
			SwapN(4);
			break;
		case op_swapd:
			SwapNM(1, 2);
			break;
		case op_swapdd:
			SwapNM(2, 3);
			break;
		case op_get1:
			GetN(1);
			break;
		case op_get2:
			GetN(2);
			break;
		case op_get3:
			GetN(3);
			break;
		case op_get4:
			GetN(4);
			break;
		case op_get5:
			GetN(5);
			break;
		case op_get6:
			GetN(6);
			break;
		case op_get7:
			GetN(7);
			break;
		case op_get8:
			GetN(8);
			break;
		case op_set1:
			SetN(1);
			break;
		case op_set2:
			SetN(2);
			break;
		case op_set3:
			SetN(3);
			break;
		case op_set4:
			SetN(4);
			break;
		case op_set5:
			SetN(5);
			break;
		case op_set6:
			SetN(6);
			break;
		case op_set7:
			SetN(7);
			break;
		case op_set8:
			SetN(8);
			break;
		case op_push_instr: 
			Unimplemented();
			break;
		case op_push_sub_short:
			Unimplemented();
			break;
		case op_stdin:
			Push(stdin);
			break;
		case op_stdout:
			Push(stdout);
			break;
		case op_stderr:
			Push(stderr);
			break;
		case op_infile:
			Pull(tmp);
			Push(fopen(tmp.As<char*>(), "r"));
			break;
		case op_outfile:
			Pull(tmp);
			Push(fopen(tmp.As<char*>(), "w"));
			break;
		case op_read:
			Unimplemented();
			break;
		case op_write:
			Unimplemented();
			break;
		case op_pipe_to:
			Unimplemented();
			break;
		case op_pipe_from:
			Unimplemented();
			break;
		case op_fork:
			Unimplemented();
			break;
		case op_make_continuation:
			Unimplemented();
			break;
		case op_clone_continuation:
			Unimplemented();
			break;
		case op_run_continuation:
			Unimplemented();
			break;
		case op_wait_on_signal:
			Unimplemented();
			break;
		case op_send_signal:
			Unimplemented();
			break;
		case op_throw:
			Unimplemented();
			break;
		case op_try:
			Unimplemented();
			break;
		case op_catch:
			Unimplemented();
			break;
		case op_finally:
			Unimplemented();
			break;
		case op_disassemble:
			Unimplemented();
			break;
		case op_assemble:
			Unimplemented();
			break;
		default:
			Error("unrecognized opcode");
			break;
	}
}